package com.mgq.java8.stream;

import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

/**
 * <b>功能：</b>Stream<br>
 * <b>Copyright mgq</b>
 * <li>版本&nbsp;&nbsp;&nbsp;&nbsp;修改日期&nbsp;&nbsp;&nbsp;&nbsp;部　　门&nbsp;&nbsp;&nbsp;&nbsp;作　者&nbsp;&nbsp;&nbsp;&nbsp;变更内容</li><hr>
 * <li>v1.0&nbsp;&nbsp;&nbsp;&nbsp;20220326&nbsp;&nbsp;营业后台&nbsp;&nbsp;&nbsp;&nbsp;马广奇&nbsp;&nbsp;&nbsp;&nbsp;创建类</li>
 * <br><br>
 *
 * @author 马广奇
 * @version 2022-03-26 19:55
 */
public class StreamTest {
    public static List<Dish> menu = Arrays.asList(
            new Dish("pork", false, 800, Dish.Type.MEAT),
            new Dish("beef", false, 700, Dish.Type.MEAT),
            new Dish("chicken", false, 400, Dish.Type.MEAT),
            new Dish("french fries", true, 530, Dish.Type.OTHER),//炸薯条
            new Dish("rice", true, 350, Dish.Type.OTHER),//米饭
            new Dish("season fruit", true, 120, Dish.Type.OTHER),//时令水果
            new Dish("pizza", true, 550, Dish.Type.OTHER),//披萨
            new Dish("prawns", false, 300, Dish.Type.FISH),//对虾
            new Dish("salmon", false, 450, Dish.Type.FISH)//三文鱼
    );

    public static void main(String[] args) {

        List<String> threeHighCaloricDishNames = menu.stream().filter(dish -> dish.getCalories() > 300)
                .map(Dish::getName)
                .limit(3)
                .collect(Collectors.toList());
        //System.out.println(threeHighCaloricDishNames);
        System.out.println("---------------------------");

        /*List<String> threeHighCaloricDishNames2 = menu.stream().filter(dish -> {
            System.out.println("filter: " + dish.getName());
            return dish.getCalories() > 300;
        }).map(dish -> {
            System.out.println("map: " + dish.getName());
            return dish.getName();
        }).limit(3)
                .collect(Collectors.toList());*/

        //System.out.println(threeHighCaloricDishNames2);

        //  predicted();
//        testFlatmap2();
        //findAndMatch();
        // testReduce();
        //testRange();
       // testGenerateStream();
        Stream<String> stream = Arrays.stream(new String[10]);
        testFlatmap();
    }

    /**
     * 用谓词筛选
     */
    public static void predicted() {
        //筛选是素食的 filter
        List<Dish> collect = menu.stream().filter(Dish::isVegetarian)
                .collect(Collectors.toList());
        //去重 distinct
        List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4);
        numbers.stream()
                .filter(i -> i % 2 == 0)
                .distinct()
                .forEach(System.out::println);
        //截断流 limit
        List<Dish> dishes = menu.stream().filter(dish -> dish.getCalories() > 300)
                .limit(3)
                .collect(Collectors.toList());
        //跳过元素 skip
        List<Dish> dishes2 = menu.stream().filter(dish -> dish.getCalories() > 300)
                .skip(2)
                .collect(Collectors.toList());
        dishes2.forEach(System.out::println);
        //映射,map 和flatmap map类似于SQL中查找某一列
        //获取name 菜名
        List<String> collect1 = menu.stream().map(Dish::getName).collect(Collectors.toList());
        //返回每个菜名的长度 [4, 4, 7, 12, 4, 12, 5, 6, 6]
        List<Integer> collect2 = menu.stream().map(Dish::getName).map(String::length).collect(Collectors.toList());
        System.out.println(collect2);

    }

    /**
     * 测试flatmap //流的扁平化 flatmap
     */
    public static void testFlatmap() {
        //flatmap 会将每一个输入对象输入映射为一个新集合，然后把这些新集合连成一个大集合。
        //
        List<String> words = Arrays.asList("Hello", "world");
        List<String> collect = words.stream().map(word -> word.split(""))
                .flatMap(Arrays::stream) //1.Arrays::stream 变为Stream<String> 2.将Stream<String>集合连接成一个新集合
                //H e l l o w o r l d
                .distinct() //去重
                //H e l o w r l d
                .collect(Collectors.toList());
        System.out.println(collect);
    }

    public static void testFlatmap2() {
        //给定2各个数字列表,返回所有的数字对 例如 [1,2,3]  [3,4] (1,3),(1,4)(2,3)....(3,4)
        List<Integer> numbers1 = Arrays.asList(1, 2, 3);
        List<Integer> numbers2 = Arrays.asList(3, 4);
        List<int[]> collect = numbers1.stream()
                .flatMap(i -> numbers2.stream().map(j -> new int[]{i, j}))
                .collect(Collectors.toList());
        System.out.println(collect);

        //以上条件,再加上能被3整除的
        List<int[]> collect1 = numbers1.stream()
                .flatMap(i -> numbers2.stream()
                        .filter(j -> (i + j) % 3 == 0)
                        .map(j -> new int[]{i, j}))
                .collect(Collectors.toList());

    }

    /**
     * 查找和匹配
     */
    public static void findAndMatch() {
        //检查谓词是否至少匹配一个元素
        if (menu.stream().anyMatch(Dish::isVegetarian)) {
            System.out.println("菜单里面有素食主义者食用的菜");
        }
        //检查谓词是否匹配所有元素  检查所有菜的热量都低于1000卡路里
        boolean isHealthy = menu.stream().allMatch(dish -> dish.getCalories() < 1000);
        //noneMatch 和 allMatch相反
        boolean isHealthy2 = menu.stream().noneMatch(dish -> dish.getCalories() >= 1000);
        System.out.println("---------------------------");
        menu.stream().filter(Dish::isVegetarian)
                .findAny()
                .ifPresent(dish -> System.out.println(dish.getName()));
    }

    /**
     * 测试reduce
     */
    public static void testReduce() {
        //reduce接收2个参数
        //1.一个初始值.
        //2.一个BinaryOperator<T> 来将两个元素结合起来产生一个新值.
        List<Integer> numbers = Arrays.asList(1, 2, 3);
        //使用reduce求和
//        Integer sum = numbers.stream().reduce(0, (a, b) -> a + b);
        Integer sum = numbers.stream().reduce(0, Integer::sum);
        System.out.println(sum);
        System.out.println("---------------------------");
        //无初始值
        Optional<Integer> reduce = numbers.stream().reduce(Integer::sum);
        reduce.ifPresent(System.out::println);
        System.out.println("---------------------------");
        //使用reduce求最大值
        numbers.stream().reduce(Integer::max).ifPresent(System.out::println);
        System.out.println("---------------------------");
        //使用reduce求最小值
        numbers.stream().reduce(Integer::min).ifPresent(System.out::println);
        System.out.println("---------------------------");
        //使用reduce计算menu中有多少个菜  可以使用map将每个元素都映射为1
        Integer reduce1 = menu.stream().map(dish -> 1).reduce(0, Integer::sum);
        System.out.println(reduce1);
    }

    /**
     * 测试数值流
     */
    public static void testNumStream() {
        //1.使用reduce会有拆箱装箱的操作,有时候我们不希望执行拆箱装箱操作.可以使用java提供的数值流
        //IntStream.DoubleStream,LongStream.分别使用对应的mapToInt() mapToDouble().mapToLong()来转换
        Integer reduce = menu.stream().map(Dish::getCalories).reduce(0, Integer::sum);
        int sum = menu.stream().mapToInt(Dish::getCalories).sum();
        //2.转换成对象流
        IntStream intStream = menu.stream().mapToInt(Dish::getCalories);
        //使用boxed()方法转换成对象流
        Stream<Integer> boxed = intStream.boxed();
    }

    /**
     * 测试数值范围和数值流的应用
     */
    public static void testRange() {
        //1.生成1到100的数字,java提供了IntStream和LongStream的静态方法
        long count = IntStream.range(1, 100).filter(n -> n % 2 == 0).count(); //(1,100)
        long count2 = IntStream.rangeClosed(1, 100).filter(n -> n % 2 == 0).count(); //[]1,100]
        System.out.println(count);
        System.out.println(count2);
        System.out.println("---------------------------");
        //数值流的应用,生成勾股数
        //[3,4,5][5,12,13]
        Stream<int[]> pythagoreanTriples = IntStream.rangeClosed(1, 100).boxed()
                .flatMap(a ->
                        IntStream.rangeClosed(a, 100)
                                //过滤求平方根是整数的数字
                                .filter(b -> Math.sqrt(a * a + b * b) % 1 == 0)
                                //将结果转换为int[]数组
                                .mapToObj(b -> new int[]{a, b, (int) Math.sqrt(a * a + b * b)})
                );
        pythagoreanTriples.limit(5).forEach(t -> System.out.println(t[0] + " ," + t[1] + " ," + t[2]));
    }

    /**
     * 测试构建流
     */
    public static void testGenerateStream() {
        //1.由值创建流  Stream.of()方法
        Stream<String> java8 = Stream.of("java8", "lambdas", "in", "action");
        java8.map(String::toUpperCase).forEach(System.out::println);

        //2.由数组创建流
        int[] numbers = {1, 2, 3, 5, 6, 7, 3, 2};
        IntStream stream = Arrays.stream(numbers);
        stream.forEach(System.out::println);
        System.out.println("---------------------------");
        //3.由文件生成流,统计单词数量
        try (Stream<String> lines = Files.lines(Paths.get("D:\\ideaworkspace\\study\\netty\\src\\main\\resources\\test.txt"), Charset.defaultCharset())) {
            long count = lines.flatMap(line -> Arrays.stream(line.split(" ")))
                    .distinct().count();
            System.out.println(count);

        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println("---------------------------");
        //4.有函数生成流:创建无限流
        //1.iterate
        //2.generate生成

        //.生成偶数
        Stream.iterate(0, n -> n + 2)
                .limit(5)
                .forEach(System.out::println);
        System.out.println("---------------------------");

        //生成斐波那契元祖序列 0,1,1,2,3,5,8,13,21
        //(0,1),(1,1)(1,2)(2,3)(3,5)
        Stream.iterate(new int[]{0, 1}, t -> new int[]{t[1], t[0] + t[1]}).limit(20)
                .forEach(t -> System.out.println("(" + t[0] + "," + t[1] + ")"));
        System.out.println("---------------------------");

        //2.generate生成. 生成0-1随机数
        Stream.generate(Math::random).limit(10).forEach(System.out::println);

    }
}
